computed 是計算屬性,要依賴其他屬性值,經過計算後返回一個值,用來處理基於數據之上的數據。只在 computed 相關依賴發生改變才會重新求值。
以下面範例來說,reversedMessage
將用作屬性 vm.reversedMessage
的 getter 函數,computed 的值在 getter 執行後是會暫存,如果 message
還沒有發生改變,多次訪問 reversedMessage
會立刻返回之前的計算結果,不必再次執行函數
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
})
computed 發生在 Vue 實例初始化的 initState 中
// src/core/instance/state.js
// ...
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
initData(vm)
} else {
observe(vm._data = {}, true)
}
/*************************************************
此處執行了 initComputed
**************************************************/
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
//...
const computedWatcherOptions = { computed: true }
/*************************************************
定義 initComputed
**************************************************/
function initComputed (vm: Component, computed: Object) {
/*************************************************
vm._computedWatchers 是一個空物件
**************************************************/
const watchers = vm._computedWatchers = Object.create(null)
const isSSR = isServerRendering()
/*************************************************
遍歷 computed 物件,拿到每一個 userDef 的 getter
**************************************************/
for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
`Getter is missing for computed property "${key}".`,
vm
)
}
if (!isSSR) {
/*************************************************
對每一個 getter 創建一個 watcher,
這裡的 watcher 是 computed watcher
**************************************************/
watchers[key] = new Watcher(
vm,
getter || noop,
noop,
computedWatcherOptions
)
}
if (!(key in vm)) {
/*************************************************
key 不是 vm 屬性,調用 defineComputed
**************************************************/
defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {
if (key in vm.$data) {
warn(`The computed property "${key}" is already defined in data.`, vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn(`The computed property "${key}" is already defined as a prop.`, vm)
}
}
}
}
/*************************************************
定義 defineComputed
**************************************************/
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop
sharedPropertyDefinition.set = userDef.set || noop
}
if (process.env.NODE_ENV !== 'production' &&
sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
`Computed property "${key}" was assigned to but it has no setter.`,
this
)
}
}
/**************************************************************************
透過 Object.defineProperty 對 computed 對應的 key 加上 getter 和 setter,
getter 最後是 createComputedGetter(key) 的返回值
***************************************************************************/
Object.defineProperty(target, key, sharedPropertyDefinition)
}
/******************************************************
定義 createComputedGetter,
createComputedGetter 返回一個 function computedGetter
*******************************************************/
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
}
}